﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices.ComTypes;
using System.Security;
using Silverlight.Hosting;

#if WPF
namespace Silverlight.Hosting.Wpf.Interop
#else
namespace Silverlight.Hosting.Interop
#endif
{
    /// <summary>
    /// Indicates the nature of a request for an Automation object.
    /// </summary>
    internal enum XcpHost_GetComAutomationObjectFlags : uint
    {
        /// <summary>
        /// Indicates that the hosted application is calling the AutomationFactory.GetObject method.
        /// </summary>
        Get = 0x1,
        /// <summary>
        /// Indicates that the hosted application is calling the AutomationFactory.CreateObject method.
        /// </summary>
        Create = 0x2
    }

    [ComImport, Guid("DFEAF541-F3E1-4C24-ACAC-99C30715084A")]
    internal class XcpControlClass
    {
    }

    [ComImport, Guid("1C3294F9-891F-49B1-BBAE-492A68BA10CC"), CoClass(typeof(XcpControlClass))]
    internal interface XcpControl : IXcpControl2
    {
    }


    [ComImport, TypeLibType((short)0x10c0), Guid("EE38D0F1-5AE3-408C-A6BF-8410E645F376")]
    public interface IXcpObject
    {
    }

    [ComImport, TypeLibType((short)0x10c0), Guid("1FB839CC-116C-4C9B-AE8E-3DBB6496E326")]
    internal interface IXcpControl
    {
        [DispId(0x20)]
        string Source { [return: MarshalAs(UnmanagedType.BStr)] [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime), TypeLibFunc((short)12), DispId(0x20)] get; [param: In, MarshalAs(UnmanagedType.BStr)] [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime), DispId(0x20), TypeLibFunc((short)12)] set; }
        [DispId(0x2e)]
        bool IsLoaded { [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime), TypeLibFunc((short)12), DispId(0x2e)] get; }
        [DispId(0x31)]
        object Content { [return: MarshalAs(UnmanagedType.IDispatch)] [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime), DispId(0x31), TypeLibFunc((short)12)] get; }
        [DispId(50)]
        object Settings { [return: MarshalAs(UnmanagedType.IDispatch)] [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime), TypeLibFunc((short)12), DispId(50)] get; }
        [DispId(0x2c)]
        object OnLoad { [return: MarshalAs(UnmanagedType.Struct)] [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime), TypeLibFunc((short)12), DispId(0x2c)] get; [param: In, MarshalAs(UnmanagedType.Struct)] [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime), TypeLibFunc((short)12), DispId(0x2c)] set; }
        [DispId(0x26)]
        object OnError { [return: MarshalAs(UnmanagedType.Struct)] [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime), TypeLibFunc((short)12), DispId(0x26)] get; [param: In, MarshalAs(UnmanagedType.Struct)] [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime), DispId(0x26), TypeLibFunc((short)12)] set; }
        [return: MarshalAs(UnmanagedType.Interface)]
        [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime), DispId(0x60020009)]
        IXcpObject CreateObject([In, MarshalAs(UnmanagedType.BStr)] string id);
        [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime), DispId(0x6002000a)]
        bool IsVersionSupported([In, MarshalAs(UnmanagedType.BStr)] string v);
        [DispId(0x33)]
        string InitParams { [return: MarshalAs(UnmanagedType.BStr)] [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime), TypeLibFunc((short)12), DispId(0x33)] get; [param: In, MarshalAs(UnmanagedType.BStr)] [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime), DispId(0x33), TypeLibFunc((short)12)] set; }
    }

    [ComImport, Guid("1C3294F9-891F-49B1-BBAE-492A68BA10CC"), TypeLibType((short)0x10c0)]
    internal interface IXcpControl2 : IXcpControl
    {
        [DispId(0x20)]
        new string Source { [return: MarshalAs(UnmanagedType.BStr)] [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime), TypeLibFunc((short)12), DispId(0x20)] get; [param: In, MarshalAs(UnmanagedType.BStr)] [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime), DispId(0x20), TypeLibFunc((short)12)] set; }
        [DispId(0x2e)]
        new bool IsLoaded { [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime), TypeLibFunc((short)12), DispId(0x2e)] get; }
        [DispId(0x31)]
        new object Content { [return: MarshalAs(UnmanagedType.IDispatch)] [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime), DispId(0x31), TypeLibFunc((short)12)] get; }
        [DispId(50)]
        new object Settings { [return: MarshalAs(UnmanagedType.IDispatch)] [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime), TypeLibFunc((short)12), DispId(50)] get; }
        [DispId(0x2c)]
        new object OnLoad { [return: MarshalAs(UnmanagedType.Struct)] [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime), DispId(0x2c), TypeLibFunc((short)12)] get; [param: In, MarshalAs(UnmanagedType.Struct)] [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime), TypeLibFunc((short)12), DispId(0x2c)] set; }
        [DispId(0x26)]
        new object OnError { [return: MarshalAs(UnmanagedType.Struct)] [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime), DispId(0x26), TypeLibFunc((short)12)] get; [param: In, MarshalAs(UnmanagedType.Struct)] [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime), DispId(0x26), TypeLibFunc((short)12)] set; }
        [return: MarshalAs(UnmanagedType.Interface)]
        [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime), DispId(0x60020009)]
        new IXcpObject CreateObject([In, MarshalAs(UnmanagedType.BStr)] string id);
        [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime), DispId(0x6002000a)]
        new bool IsVersionSupported([In, MarshalAs(UnmanagedType.BStr)] string v);
        [DispId(0x33)]
        new string InitParams { [return: MarshalAs(UnmanagedType.BStr)] [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime), TypeLibFunc((short)12), DispId(0x33)] get; [param: In, MarshalAs(UnmanagedType.BStr)] [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime), DispId(0x33), TypeLibFunc((short)12)] set; }
        [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime), DispId(0x60030000)]
        void LoadRuntime();
        [DispId(0x37)]
        string SplashScreenSource { [return: MarshalAs(UnmanagedType.BStr)] [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime), TypeLibFunc((short)12), DispId(0x37)] get; [param: In, MarshalAs(UnmanagedType.BStr)] [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime), TypeLibFunc((short)12), DispId(0x37)] set; }
        [DispId(0x39)]
        object OnSourceDownloadComplete { [return: MarshalAs(UnmanagedType.Struct)] [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime), DispId(0x39), TypeLibFunc((short)12)] get; [param: In, MarshalAs(UnmanagedType.Struct)] [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime), TypeLibFunc((short)12), DispId(0x39)] set; }
        [DispId(0x38)]
        object OnSourceDownloadProgressChanged { [return: MarshalAs(UnmanagedType.Struct)] [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime), DispId(0x38), TypeLibFunc((short)12)] get; [param: In, MarshalAs(UnmanagedType.Struct)] [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime), TypeLibFunc((short)12), DispId(0x38)] set; }
    }

    [ComImport]
    [Guid("2E340632-5D5A-427b-AC31-303F6E32B9E8")]
    [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
    internal interface IXcpControlDownloadCallback
    {
        void OnUrlDownloaded([In] int hr, [In, MarshalAs(UnmanagedType.Interface)] IStream stream);
    }

    [ComImport]
    [Guid("1B36028E-B491-4bb2-8584-8A9E0A677D6E")]
    [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
    internal interface IXcpControlHost
    {
        [return: MarshalAs(UnmanagedType.U4)]
        XcpHostOptions GetHostOptions();
        void NotifyLoaded();
        void NotifyError([In, MarshalAs(UnmanagedType.BStr)] string error, [In, MarshalAs(UnmanagedType.BStr)] string source, [In] int line, [In] int column);
        [return: MarshalAs(UnmanagedType.IUnknown)]
        object InvokeHandler([In, MarshalAs(UnmanagedType.BStr)] string name, [In] object param1, [In] object param2);
        [return: MarshalAs(UnmanagedType.BStr)]
        string GetBaseUrl();
        [return: MarshalAs(UnmanagedType.BStr)]
        string GetNamedSource([In, MarshalAs(UnmanagedType.BStr)] string sourceName);
        [PreserveSig]
        int DownloadUrl([In, MarshalAs(UnmanagedType.BStr)] string url, [In, MarshalAs(UnmanagedType.Interface)] IXcpControlDownloadCallback callback, [Out, MarshalAs(UnmanagedType.Interface)] out IStream stream);
    }

    [ComImport]
    [Guid("fb3ed7c4-5797-4b44-8695-0c512ea27d8f")]
    [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
    internal interface IXcpControlHost2 : IXcpControlHost
    {
        [return: MarshalAs(UnmanagedType.U4)]
        new XcpHostOptions GetHostOptions();
        new void NotifyLoaded();
        new void NotifyError([In, MarshalAs(UnmanagedType.BStr)] string error, [In, MarshalAs(UnmanagedType.BStr)] string source, [In] int line, [In] int column);
        [return: MarshalAs(UnmanagedType.IUnknown)]
        new object InvokeHandler([In, MarshalAs(UnmanagedType.BStr)] string name, [In] object param1, [In] object param2);
        [return: MarshalAs(UnmanagedType.BStr)]
        new string GetBaseUrl();
        [return: MarshalAs(UnmanagedType.BStr)]
        new string GetNamedSource([In, MarshalAs(UnmanagedType.BStr)] string sourceName);
        [PreserveSig]
        new int DownloadUrl([In, MarshalAs(UnmanagedType.BStr)] string url, [In, MarshalAs(UnmanagedType.Interface)] IXcpControlDownloadCallback callback, [Out, MarshalAs(UnmanagedType.Interface)] out IStream stream);

        void GetCustomAppDomain([Out, MarshalAs(UnmanagedType.IUnknown)] out object appDomain);
        void GetControlVersion(ref uint majorVersion, ref uint minorVersion);
    }

    [ComImport]
    [Guid("9fb2ce5f-06ff-4058-befa-ddfab596b3d5")]
    [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
    internal interface IXcpControlHost3 : IXcpControlHost2
    {
        [return: MarshalAs(UnmanagedType.U4)]
        new XcpHostOptions GetHostOptions();
        new void NotifyLoaded();
        new void NotifyError([In, MarshalAs(UnmanagedType.BStr)] string error, [In, MarshalAs(UnmanagedType.BStr)] string source, [In] int line, [In] int column);
        [return: MarshalAs(UnmanagedType.IUnknown)]
        new object InvokeHandler([In, MarshalAs(UnmanagedType.BStr)] string name, [In] object param1, [In] object param2);
        [return: MarshalAs(UnmanagedType.BStr)]
        new string GetBaseUrl();
        [return: MarshalAs(UnmanagedType.BStr)]
        new string GetNamedSource([In, MarshalAs(UnmanagedType.BStr)] string sourceName);
        [PreserveSig]
        new int DownloadUrl([In, MarshalAs(UnmanagedType.BStr)] string url, [In, MarshalAs(UnmanagedType.Interface)] IXcpControlDownloadCallback callback, [Out, MarshalAs(UnmanagedType.Interface)] out IStream stream);
        new void GetCustomAppDomain([Out, MarshalAs(UnmanagedType.IUnknown)] out object appDomain);
        new void GetControlVersion(ref uint majorVersion, ref uint minorVersion);
    
        [return: MarshalAs(UnmanagedType.BStr)]
        string GetDefaultThemeXaml([In, MarshalAs(UnmanagedType.BStr)] string assemblyName);
        [return: MarshalAs(UnmanagedType.BStr)]
        string GetDefaultPortableUserInterfaceFontInfo();
        [PreserveSig]
        int GetComAutomationObject([In, MarshalAs(UnmanagedType.BStr)] string progId,
            [In, MarshalAs(UnmanagedType.U4)] XcpHost_GetComAutomationObjectFlags flags,
            [Out, MarshalAs(UnmanagedType.IDispatch)] out object automationObject);
    }

    [ComImport, Guid("ed114498-ed0c-46c9-8609-e480a7e8b085"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
    internal interface ISilverlightViewerProvider
    {
        [PreserveSig]
        int GetSilverlightViewer(ref IntPtr ppSilverlightViewer);
    }

    internal abstract class AlmostUnknown
    {
        private readonly IntPtr m_structPointer;

        private delegate uint AddRefDelegate(IntPtr pStruct);
        private delegate uint ReleaseDelegate(IntPtr pStruct);

        private AddRefDelegate AddRefMethod;
        private ReleaseDelegate ReleaseMethod;

        protected AlmostUnknown(IntPtr pStruct)
        {
            m_structPointer = pStruct;
            SetupAlmostUnknownMethods();
        }

        private void SetupAlmostUnknownMethods()
        {
            /* Read the COM v-table pointer */
            IntPtr pVtable = Marshal.ReadIntPtr(m_structPointer);

            const int ADDREF_VTABLE_INDEX = 0;
            const int RELEASE_VTABLE_INDEX = 1;

            /* Get the function pointer */
            IntPtr pFunc = Marshal.ReadIntPtr(pVtable, ADDREF_VTABLE_INDEX * IntPtr.Size);

            /* Cast the function pointer to a .NET delegate */
            AddRefMethod = (AddRefDelegate)Marshal.GetDelegateForFunctionPointer(pFunc, typeof(AddRefDelegate));

            /* Rinse and repeat */
            pFunc = Marshal.ReadIntPtr(pVtable, RELEASE_VTABLE_INDEX * IntPtr.Size);

            ReleaseMethod = (ReleaseDelegate)Marshal.GetDelegateForFunctionPointer(pFunc, typeof(ReleaseDelegate));
        }

        public uint AddRef()
        {
            return AddRefMethod(m_structPointer);
        }

        public uint Release()
        {
            return ReleaseMethod(m_structPointer);
        }

        public IntPtr StructPointer
        {
            get { return m_structPointer; }
        }
    }

    internal class SilverlightViewer : AlmostUnknown
    {
        private delegate void SuppressBrowserEventsDelegate(IntPtr pStruct, int bSuppress);
        private delegate int CreateRenderTargetBitmapDelegate(IntPtr pStruct, uint uBackground, int nOffsetX, int nOffsetY, uint nWidth, uint nHeight, IntPtr pvBits, out IntPtr pplRenderTargetBitmap);
        private delegate int ProcessInputDelegate(IntPtr pStruct, uint msg, uint wParam, uint lParam, ref IntPtr curEvent, int x, int y, out uint fHandled);

        private SuppressBrowserEventsDelegate m_suppressBrowserEvents;
        private CreateRenderTargetBitmapDelegate m_createRenderTargetBitmap;
        private ProcessInputDelegate m_processInput;

        public SilverlightViewer(IntPtr pStruct)
            : base(pStruct)
        {
            SetupSilverlightViewerMethods();
        }

        private void SetupSilverlightViewerMethods()
        {
            /* Read the COM v-table pointer */
            IntPtr pVtable = Marshal.ReadIntPtr(StructPointer);

            const int SUPPRESS_BROWSER_EVENTS_VTABLE_INDEX = 2;
            const int CREATE_RENDER_TARGET_BITMAP = 3;
            const int PROCESS_INPUT_DELEGATE = 4;

            /* Get the function pointer */
            IntPtr pFunc = Marshal.ReadIntPtr(pVtable, SUPPRESS_BROWSER_EVENTS_VTABLE_INDEX * IntPtr.Size);

            /* Cast the function pointer to a .NET delegate */
            m_suppressBrowserEvents = (SuppressBrowserEventsDelegate)Marshal.GetDelegateForFunctionPointer(pFunc, typeof(SuppressBrowserEventsDelegate));

            /* Rinse and repeat */
            pFunc = Marshal.ReadIntPtr(pVtable, CREATE_RENDER_TARGET_BITMAP * IntPtr.Size);
            m_createRenderTargetBitmap = (CreateRenderTargetBitmapDelegate)Marshal.GetDelegateForFunctionPointer(pFunc, typeof(CreateRenderTargetBitmapDelegate));

            /* Rinse and repeat */
            pFunc = Marshal.ReadIntPtr(pVtable, PROCESS_INPUT_DELEGATE * IntPtr.Size);
            m_processInput = (ProcessInputDelegate)Marshal.GetDelegateForFunctionPointer(pFunc, typeof(ProcessInputDelegate));
        }

        public void SuppressBrowserEvents(int bSuppress)
        {
            m_suppressBrowserEvents(StructPointer, bSuppress);
        }

        public int CreateRenderTargetBitmap(uint uBackground, int nOffsetX, int nOffsetY, uint nWidth, uint nHeight, IntPtr pvBits, out IntPtr pplRenderTargetBitmap)
        {
            int hr = m_createRenderTargetBitmap(StructPointer, uBackground, nOffsetX, nOffsetY, nWidth, nHeight, pvBits, out pplRenderTargetBitmap);

            return hr;
        }

        public int ProcessInput(uint msg, uint wParam, uint lParam, ref IntPtr curEvent, int x, int y, out uint fHandled)
        {
            int hr =  m_processInput(StructPointer, msg, wParam, lParam, ref curEvent, x, y, out fHandled);

            return hr;
        }
    }

    [StructLayout(LayoutKind.Sequential)]
    internal struct XRECT
    {
        public int X;
        public int Y;
        public int Width;
        public int Height;

        public System.Drawing.Rectangle ToRectangle()
        {
            return new System.Drawing.Rectangle(X, Y, Width, Height);
        }
    };

    internal class RenderTargetBitmap : AlmostUnknown
    {
        [SuppressUnmanagedCodeSecurity]
        private delegate int RenderDelegate(IntPtr pStruct, IntPtr pElement, [Out, MarshalAs(UnmanagedType.Struct)]out XRECT prcDirtyRect);
        private RenderDelegate m_render;

        public RenderTargetBitmap(IntPtr pStruct)
            : base(pStruct)
        {
            SetupRenderTargetBitmapMethods();
        }

        private void SetupRenderTargetBitmapMethods()
        {
            /* Read the COM v-table pointer */
            IntPtr pVtable = Marshal.ReadIntPtr(StructPointer);

            const int RENDER_VTABLE_INDEX = 2;

            /* Get the function pointer */
            IntPtr pFunc = Marshal.ReadIntPtr(pVtable, RENDER_VTABLE_INDEX * IntPtr.Size);

            /* Cast the function pointer to a .NET delegate */
            m_render = (RenderDelegate)Marshal.GetDelegateForFunctionPointer(pFunc, typeof(RenderDelegate));
        }

        public int Render(IntPtr pElement, out XRECT prcDirtyRect)
        {
            return m_render(StructPointer, pElement, out prcDirtyRect);
        }
    }

}
